﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.IO;

using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Content;

using GDIBitmap = System.Drawing.Bitmap;
using GDIColor = System.Drawing.Color;
using GDIImage = System.Drawing.Image;
using GDIGraphics = System.Drawing.Graphics;
using GDIGraphicsUnit = System.Drawing.GraphicsUnit;
using GDIRectangle = System.Drawing.Rectangle;

using RpgLibrary;
using RpgLibrary.WorldClasses;
using RpgLibrary.CharacterClasses;
using RpgLibrary.SpriteClasses;
using XRpgLibrary.CharacterClasses;
using XRpgLibrary.TileEngine;
using XRpgLibrary.SpriteClasses;
using XRpgLibrary.ScriptClasses;

using XLevelEditor.Controls;
using XRpgLibrary.ExtensionMethods;

namespace XLevelEditor
{
    public enum BrushMode
    {
        TileBrush,
        TileFill,
        CharacterBrush,
        PassabilityBrush
    }

    public enum PassWall
    {
        North,
        South,
        East,
        West
    }

    public partial class FormMain : Form
    {
        #region Field Region

        ContentManager contentManager;

        SpriteBatch spriteBatch;
        SpriteBatch minimapSpriteBatch;

        LevelData levelData;
        TileMap map;
        
        List<Tileset> tileSets = new List<Tileset>();
        BindingList<MapLayer> layers = new BindingList<MapLayer>();
        List<GDIImage> tileSetImages = new List<GDIImage>();
        List<TilesetData> tileSetData = new List<TilesetData>();

        Camera camera;
        Engine engine;
        
        Point mouse = new Point();
        
        bool isLeftMouseDown = false;
        bool isRightMouseDown = false;

        bool trackMouse = false;
        int brushWidth = 1;

        Texture2D cursor;
        Texture2D grid;
        Texture2D borderTexture;
        Texture2D shadow;
        Texture2D passTexture;
        Texture2D currentEntityTexture;

        Color gridColor = Color.White;
        Color transparentColor = Color.CornflowerBlue;

        Vector2 shadowPosition = Vector2.Zero;
        Vector2 mousePosition = Vector2.Zero;

        BrushMode brushMode = BrushMode.TileBrush;

        Point lastTile = new Point(-1,-1);

        OpenFileDialog openLevelDialog;

        string InvisibleEntityTextureName = @"PlayerSprites\entity-invisible";

        Tileset ActiveTileset
        {
            get
            {
                return tileSets[cbTileset.SelectedIndex];
            }
        }

        Character ActiveCharacter
        {
            get
            {
                return (Character)lbCharacters.SelectedItem;
            }
            set
            {
                lbCharacters.SelectedItem = value;
            }
        }

        bool keyControlPressed = false;
        bool keyAltPressed = false;

        #endregion
        
        #region Property Region

        private string LevelFileName
        {
            get
            {
                return OpenLevelDialog.FileName;
            }
            set
            {
                OpenLevelDialog.FileName = value;
            }
        }

        private OpenFileDialog OpenLevelDialog
        {
            get
            {
                if (openLevelDialog == null)
                {
                    openLevelDialog = new OpenFileDialog();
                    openLevelDialog.Filter = "Level Files (*.xml)|*.xml";
                    openLevelDialog.CheckFileExists = true;
                    openLevelDialog.CheckPathExists = true;
                    if (!string.IsNullOrEmpty(Program.PreviouslyLoadedGamePath))
                        openLevelDialog.InitialDirectory = Path.GetDirectoryName(Program.PreviouslyLoadedGamePath);
                }

                return openLevelDialog;
            }
        }

        MapLayer ActiveLayer
        {
            get
            {
                if (mapLayerView.Rows.Count > 0)
                {
                    if (mapLayerView.SelectedRows.Count < 1)
                    {
                        mapLayerView.Rows[0].Selected = true;
                    }
                    return (MapLayer)mapLayerView.SelectedRows[0].DataBoundItem;
                }

                return null;
            }
            set
            {
                if (value == null)
                {
                    mapLayerView.Rows[0].Selected = true;
                }
                
                for(int i = 0, cnt = layers.Count; i < cnt; i++)
                    if (((MapLayer)mapLayerView.Rows[i].DataBoundItem) == value)
                    {
                        mapLayerView.SelectRow(i);
                        break;
                    }
            }
        }

        MapLayer ActivePassabilityLayer
        {
            get
            {
                if (cboPaintPassabilityLayer.Items.Count > 0)
                {
                    if (cboPaintPassabilityLayer.SelectedIndex < 0)
                    {
                        cboPaintPassabilityLayer.SelectedIndex = 0;
                    }

                    return layers[cboPaintPassabilityLayer.SelectedIndex];
                }

                return null;
            }
            set
            {
                if (value == null)
                {
                    cboPaintPassabilityLayer.SelectedIndex = 0;
                }

                for (int i = 0, cnt = layers.Count; i < cnt; i++)
                    if (((MapLayer)cboPaintPassabilityLayer.Items[i]) == value)
                    {
                        cboPaintPassabilityLayer.SelectedIndex = i;
                        break;
                    }
            }
        }

        private Texture2D InvisibleEntityTexture
        {
            get
            {
                return contentManager.Load<Texture2D>(InvisibleEntityTextureName);
            }
        }

        private Texture2D CurrentEntityTexture
        {
            get
            {
                if (currentEntityTexture == null)
                    currentEntityTexture = BuildCurrentEntityTexture();

                return currentEntityTexture;
            }
        }

        private Texture2D PassTexture
        {
            get
            {
                if (passTexture == null)
                    passTexture = BuildPassTexture();

                return passTexture;
            }
        }

        public GraphicsDevice GraphicsDevice
        {
            get { return mapDisplay.GraphicsDevice; }
        }

        public GraphicsDevice MiniMapGraphicsDevice
        {
            get { return minimapDisplay.GraphicsDevice; }
        }

        #endregion
        
        #region Constructor Region

        public FormMain()
        {
            InitializeComponent();    
            contentManager = new ContentManager(mapDisplay.Services, "Content");
        }

        #endregion

        #region Grid Color Event Handler Region

        void setGridColor(Color color, ToolStripMenuItem colorMenuItem)
        {
            gridColor = color;

            foreach (ToolStripMenuItem item in gridColorToolStripMenuItem.DropDownItems)
                item.Checked = item == colorMenuItem;
        }

        void whiteToolStripMenuItem_Click(object sender, EventArgs e)
        {
            setGridColor(Color.White, (ToolStripMenuItem)sender);
        }

        void yellowToolStripMenuItem_Click(object sender, EventArgs e)
        {
            setGridColor(Color.Yellow, (ToolStripMenuItem)sender);
        }
        void greenToolStripMenuItem_Click(object sender, EventArgs e)
        {
            setGridColor(Color.Green, (ToolStripMenuItem)sender);
        }
        void redToolStripMenuItem_Click(object sender, EventArgs e)
        {
            setGridColor(Color.Red, (ToolStripMenuItem)sender);
        }
        void blueToolStripMenuItem_Click(object sender, EventArgs e)
        {
            setGridColor(Color.Blue, (ToolStripMenuItem)sender);
        }
        void blackToolStripMenuItem_Click(object sender, EventArgs e)
        {
            setGridColor(Color.Black, (ToolStripMenuItem)sender);
        }

        #endregion

        #region Brush Event Handler Region

        void x1ToolStripMenuItem_Click(object sender, EventArgs e)
        {
            x1ToolStripMenuItem.Checked = true;
            x2ToolStripMenuItem.Checked = false;
            x4ToolStripMenuItem.Checked = false;
            x8ToolStripMenuItem.Checked = false;
            brushWidth = 1;
        }

        void x2ToolStripMenuItem_Click(object sender, EventArgs e)
        {
            x1ToolStripMenuItem.Checked = false;
            x2ToolStripMenuItem.Checked = true;
            x4ToolStripMenuItem.Checked = false;
            x8ToolStripMenuItem.Checked = false;
            brushWidth = 2;
        }

        void x4ToolStripMenuItem_Click(object sender, EventArgs e)
        {
            x1ToolStripMenuItem.Checked = false;
            x2ToolStripMenuItem.Checked = false;
            x4ToolStripMenuItem.Checked = true;
            x8ToolStripMenuItem.Checked = false;
            brushWidth = 4;
        }

        void x8ToolStripMenuItem_Click(object sender, EventArgs e)
        {
            x1ToolStripMenuItem.Checked = false;
            x2ToolStripMenuItem.Checked = false;
            x4ToolStripMenuItem.Checked = false;
            x8ToolStripMenuItem.Checked = true;
            brushWidth = 8;
        }

        #endregion

        void openLevelToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (OpenLevelDialog.ShowDialog() != DialogResult.OK)
                return;

            Program.PreviouslyLoadedGamePath = OpenLevelDialog.FileName;

            OpenLevel(OpenLevelDialog.FileName);
        }

        private void OpenLevel(string levelFilePath)
        {
            string path = Path.GetDirectoryName(levelFilePath);

            levelData = XnaSerializer.Deserialize<LevelData>(levelFilePath);
            MapData mapData = XnaSerializer.Deserialize<MapData>(Path.Combine(path, "Maps", levelData.MapName + ".xml"));

            ClearEnvironment();

            if (LoadTilesetsFromData(mapData))
            {
                LoadLayersFromData(mapData, null);

                map = new TileMap(tileSets, layers.ToList<MapLayer>());

                SetMinimapSize();
                InitializeMapScroll();
                ActivateMenu();
            }
            else
            {
                ClearEnvironment();
            }
        }

        private void LoadLayersFromData(MapData mapData, ScriptContext scriptContext)
        {
            foreach (MapLayerData data in mapData.Layers)
                layers.Add(MapLayer.FromLayerData(data, spriteBatch, contentManager, scriptContext, null));

            mapLayerView.SelectFirstRow();
            cboPaintPassabilityLayer.SelectFirstItem();
        }

        private bool LoadTilesetsFromData(MapData mapData)
        {
            foreach (TilesetData data in mapData.Tilesets)
            {
                Texture2D texture = null;
                Texture2D passTexture = null;

                tileSetData.Add(data);
                cbTileset.Items.Add(data.TilesetName);

                if (contentManager.ContentExists(data.TilesetImageName))
                {
                    texture = contentManager.Load<Texture2D>(data.TilesetImageName);
                    tileSetImages.Add(texture.ToImage());
                }
                else
                {
                    MessageBox.Show(string.Format("Failed to load tileset image '{0}'. Aborting level load.", data.TilesetImageName),
                        "Tileset Load Failed",
                        MessageBoxButtons.OK,
                        MessageBoxIcon.Error);
                    return false;
                }

                if (contentManager.ContentExists(data.TilesetPassabilityImageName))
                    passTexture = contentManager.Load<Texture2D>(data.TilesetPassabilityImageName);

                tileSets.Add(
                    new Tileset(
                        texture,
                        passTexture,
                        data.TilesWide,
                        data.TilesHigh,
                        data.TileWidthInPixels,
                        data.TileHeightInPixels,
                        data.AnimationScript));
            }
            
            cbTileset.SelectedIndex = 0;

            return true;
        }

        private void ClearEnvironment()
        {
            tileSetImages.Clear();
            tileSetData.Clear();
            tileSets.Clear();
            layers.Clear();
            cbTileset.Items.Clear();
            cbTileset.Text = null;
            tileSelector.TilesetTexture = null;
            map = null;
            ActiveCharacter = null;
            currentEntityTexture = null;
            PopulateActiveCharacterData();
        }

        private void ActivateMenu()
        {
            foreach (ToolStripItem item in menuStrip1.Items)
                item.Enabled = true;
        }

        #region Save Menu Item Event Handler Region
        
        void saveLevelToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (map == null)
                return;

            string gamePath = string.Empty;
            FolderBrowserDialog fbDialog = null;
            if (string.IsNullOrEmpty(LevelFileName))
            {
                fbDialog = new FolderBrowserDialog();
                fbDialog.Description = "Select Game Folder";
                fbDialog.SelectedPath = Application.StartupPath;
            }
            else
            {
                gamePath = Path.GetDirectoryName(LevelFileName);
                gamePath = gamePath.Substring(0, gamePath.LastIndexOf('\\'));
            }

            if (string.IsNullOrEmpty(gamePath))
                if (fbDialog.ShowDialog() == DialogResult.OK)
                    gamePath = fbDialog.SelectedPath;
                else
                    return;

            SaveLevel(gamePath);
        }

        private void SaveLevel(string gamePath)
        {
            if (!File.Exists(Path.Combine(gamePath, "Game.xml")))
            {
                MessageBox.Show("Game not found", "Error");
                return;
            }

            MapData mapData = new MapData(
                levelData.MapName,
                tileSetData,
                GenerateLayerData());

            string LevelPath = Path.Combine(gamePath, @"Levels\");
            string MapPath = Path.Combine(LevelPath, @"Maps\");

            if (!Directory.Exists(LevelPath))
                Directory.CreateDirectory(LevelPath);

            if (!Directory.Exists(MapPath))
                Directory.CreateDirectory(MapPath);

            XnaSerializer.Serialize<LevelData>(Path.Combine(LevelPath, levelData.LevelName + ".xml"), levelData);
            XnaSerializer.Serialize<MapData>(Path.Combine(MapPath, mapData.MapName + ".xml"), mapData);

            Program.PreviouslyLoadedGamePath = Path.Combine(LevelPath, levelData.LevelName + ".xml");

        }

        private List<MapLayerData> GenerateLayerData()
        {
            List<MapLayerData> layerData = new List<MapLayerData>();

            for (int i = 0, cnt = layers.Count; i < cnt; i++)
                layerData.Add(layers[i].GetDataObject());

            return layerData;
        }

        #endregion

        #region Form Event Handler Region
        
        void FormMain_Load(object sender, EventArgs e)
        {
            mapLayerView.DataSource = layers;

            cboPaintPassabilityLayer.BindingContext = new BindingContext();
            cboPaintPassabilityLayer.DataSource = layers;
            cboPaintPassabilityLayer.DisplayMember = "Name";

            mapLayerView.SelectionChanged += new EventHandler(mapLayerView_SelectionChanged);
            cbTileset.SelectedIndexChanged += new EventHandler(cbTileset_SelectedIndexChanged);

            Rectangle viewPort = new Rectangle(0, 0, mapDisplay.Width, mapDisplay.Height);
            camera = new Camera(viewPort);

            engine = new Engine(RpgConstants.TileWidth, RpgConstants.TileHeight);
            
            controlTimer.Tick += new EventHandler(controlTimer_Tick);
            
            mapDisplay.SizeChanged += new EventHandler(mapDisplay_SizeChanged);

            if (!string.IsNullOrEmpty(Program.PreviouslyLoadedGamePath) 
                && File.Exists(Program.PreviouslyLoadedGamePath))
            {
                OpenLevelDialog.FileName = Program.PreviouslyLoadedGamePath;
                OpenLevel(Program.PreviouslyLoadedGamePath);
            }
        }

        void mapLayerView_SelectionChanged(object sender, EventArgs e)
        {
            if (mapLayerView.Rows.Count > 0 )
                BindCharacterList();
        }

        void mapDisplay_SizeChanged(object sender, EventArgs e)
        {
            Rectangle viewPort = new Rectangle(0, 0, mapDisplay.Width, mapDisplay.Height);
            Vector2 cameraPosition = camera.Position;
            camera = new Camera(viewPort, cameraPosition);

            SetMinimapPosition();
            InitializeMapScroll();
            mapDisplay.Invalidate();
        }

        void controlTimer_Tick(object sender, EventArgs e)
        {
            mapDisplay.Invalidate();
            minimapDisplay.Invalidate();
            Logic();
        }
        
        #endregion

        #region Tile Tab Event Handler Region

        void cbTileset_SelectedIndexChanged(object sender, EventArgs e)
        {
            if (ActiveTileset != null)
                FillPreviews();

            tbTilesetAnimationScript.Text = ActiveTileset.AnimationScript.Replace("\n", "\r\n");
        }

        private Texture2D BuildCurrentEntityTexture()
        {
            string textureName = GetCurrentTextureName();

            if (!string.IsNullOrEmpty(textureName) && contentManager.ContentExists(textureName))
            {
                return contentManager.Load<Texture2D>(textureName);
            }
            
            return null;
        }

        private string GetCurrentTextureName()
        {
            string textureName = string.Empty;

            if (!string.IsNullOrEmpty(tbCharacterSprite.Text))
            {
                if (contentManager.ContentExists(tbCharacterSprite.Text))
                    textureName = tbCharacterSprite.Text;
            }
            else if (rbCharacterClone.Checked && ActiveCharacter != null)
            {
                textureName = ActiveCharacter.Sprite.TextureImageName;
            }
            else if (rbCharacterCreate.Checked)
            {
                textureName = InvisibleEntityTextureName;
            }

            return textureName;
        }

        private Texture2D BuildPassTexture()
        {
            Texture2D texture = new Texture2D(GraphicsDevice, 1, 1);
            texture.SetData<Color>(new Color[] { new Color ( 255, 0, 0, 25 ) });

            return texture;
        }

        private void FillPreviews()
        {
            tileSelector.TilesetTexture = tileSets[cbTileset.SelectedIndex].Texture;
        }
        #endregion

        #region New Menu Item Event Handler Region

        void newLevelToolStripMenuItem_Click(object sender, EventArgs e)
        {
            using (FormNewLevel frmNewLevel = new FormNewLevel())
            {
                frmNewLevel.ShowDialog();

                if (frmNewLevel.DialogResult == DialogResult.OK)
                {
                    ClearEnvironment();
                    levelData = frmNewLevel.LevelData;
                    SetMinimapSize();
                    InitializeMapScroll();
                    ActivateMenu();
                    CreateNewTileset();
                    CreateNewLayer();
                }
            }
        }
        
        void newTilesetToolStripMenuItem_Click(object sender, EventArgs e)
        {
            CreateNewTileset();
        }

        private void CreateNewTileset()
        {
            using (FormNewTileset frmNewTileset = new FormNewTileset())
            {
                frmNewTileset.ShowDialog();

                if (frmNewTileset.DialogResult == DialogResult.OK)
                {
                    TilesetData data = frmNewTileset.TilesetData;

                    if (contentManager.ContentExists(data.TilesetImageName))
                    {
                        Texture2D texture = contentManager.Load<Texture2D>(data.TilesetImageName);
                        tileSetImages.Add(texture.ToImage());

                        Texture2D passTexture = null;

                        if (contentManager.ContentExists(data.TilesetPassabilityImageName))
                        {
                            passTexture = contentManager.Load<Texture2D>(data.TilesetPassabilityImageName);
                        }
                        else
                        {
                            MessageBox.Show(
                                "Default passability data not found, so none loaded.",
                                "Pass data missing",
                                MessageBoxButtons.OK,
                                MessageBoxIcon.Information);
                        }

                        Tileset tileset = new Tileset(
                            texture,
                            passTexture,
                            data.TilesWide,
                            data.TilesHigh,
                            data.TileWidthInPixels,
                            data.TileHeightInPixels,
                            data.AnimationScript);

                        tileSets.Add(tileset);
                        tileSetData.Add(data);

                        if (map != null)
                            map.AddTileset(tileset);

                        cbTileset.Items.Add(data.TilesetName);

                        if (cbTileset.SelectedItem == null)
                            cbTileset.SelectedIndex = 0;

                        mapLayerToolStripMenuItem.Enabled = true;
                    }
                    else
                    {
                        MessageBox.Show(string.Format("This tileset image '{0}' was not found, create new tileset aborted", data.TilesetImageName));
                    }
                }
            }
        }

        void newLayerToolStripMenuItem_Click(object sender, EventArgs e)
        {
            CreateNewLayer();
        }

        private void CreateNewLayer()
        {
            if (tileSets == null || tileSets.Count < 1)
            {
                if (MessageBox.Show(
                        "You can't create a Map Layer until you create at least one Tileset. Would you like to create one now?",
                        "Create New Tileset?",
                        MessageBoxButtons.YesNo,
                        MessageBoxIcon.Question) == DialogResult.Yes)
                    CreateNewTileset();
                
                return;
            }

            using (FormNewLayer frmNewLayer = new FormNewLayer(levelData.MapWidth, levelData.MapHeight, tileSets, cbTileset.Items))
            {
                frmNewLayer.ShowDialog();

                if (frmNewLayer.DialogResult == DialogResult.OK)
                {
                    MapLayerData data = frmNewLayer.LayerData;

                    MapLayer layer = MapLayer.FromLayerData(data, spriteBatch, contentManager, null, null);

                    layers.Insert(0, layer);
                    mapLayerView.SelectFirstRow();
                    cboPaintPassabilityLayer.SelectedIndex = 0;

                    if (map == null)
                        map = new TileMap(tileSets, layers.ToList<MapLayer>());

                    SetMinimapSize();
                    ActivateMenu();
                }
            }
        }

        #endregion
        
        #region Map Display Event Handler Region
        
        void mapDisplay_OnInitialize(object sender, EventArgs e)
        {
            spriteBatch = new SpriteBatch(GraphicsDevice);

            shadow = BuildShadow();

            borderTexture = new Texture2D(GraphicsDevice, 1, 1);
            borderTexture.SetData<Color>(new Color[] { Color.White });

            grid = contentManager.Load<Texture2D>(@"Editor\grid");
            cursor = contentManager.Load<Texture2D>(@"Editor\cursor");

            mapDisplay.MouseEnter += new EventHandler(mapDisplay_MouseEnter);
            mapDisplay.MouseLeave += new EventHandler(mapDisplay_MouseLeave);
            mapDisplay.MouseMove += new MouseEventHandler(mapDisplay_MouseMove);
            mapDisplay.MouseDown += new MouseEventHandler(mapDisplay_MouseDown);
            mapDisplay.MouseUp += new MouseEventHandler(mapDisplay_MouseUp);    
        }

        private Texture2D BuildShadow()
        {
            Texture2D shadow = new Texture2D(
                GraphicsDevice,
                RpgConstants.TileWidth,
                RpgConstants.TileHeight,
                false,
                SurfaceFormat.Color);

            Color[] data = new Color[shadow.Width * shadow.Height];
            Color tint = Color.White;
            tint.A = 25;

            for (int i = 0, cnt = shadow.Width * shadow.Height; i < cnt; i++)
                data[i] = tint;

            shadow.SetData<Color>(data);

            return shadow;
        }


        void minimapDisplay_OnInitialize(object sender, EventArgs e)
        {
            minimapSpriteBatch = new SpriteBatch(MiniMapGraphicsDevice);
            SetMinimapSize();
        }

        private void SetMinimapSize()
        {
            if (map != null)
            {
                minimapDisplay.Width = map.MapWidth + 2;
                minimapDisplay.Height = map.MapHeight + 2;
                SetMinimapPosition();
            }
        }

        private void SetMinimapPosition()
        {
            btnShowMiniMap.Top = mapDisplay.Bottom - btnShowMiniMap.Height - mapScrollH.Height - 5;
            minimapDisplay.Top = btnShowMiniMap.Top - minimapDisplay.Height - 2; 
        }

        void minimapDisplay_OnDraw(object sender, EventArgs e)
        {
            GraphicsDevice.Clear(Color.Black);
            RenderMini();
        }

        void mapDisplay_OnDraw(object sender, EventArgs e)
        {
            GraphicsDevice.Clear(transparentColor);
            Render();
        }
        
        void mapDisplay_MouseUp(object sender, MouseEventArgs e)
        {
            lastTile = new Point(-1, -1);

            if (e.Button == MouseButtons.Left)
                isLeftMouseDown = false;

            if (e.Button == MouseButtons.Right)
                isRightMouseDown = false;
        }

        void mapDisplay_MouseDown(object sender, MouseEventArgs e)
        {
            if (e.Button == MouseButtons.Left)
                isLeftMouseDown = true;

            if (e.Button == MouseButtons.Right)
                isRightMouseDown = true;
        }

        void minimapDisplay_MouseMove(object sender, MouseEventArgs e)
        {
            if (e.Button == MouseButtons.Left)
            {
                camera.Position = new Vector2(
                    e.X * Engine.TileWidth * camera.Zoom, 
                    e.Y * Engine.TileHeight * camera.Zoom);
                camera.LockCamera();
                InitializeMapScroll();
            }
        }

        void mapDisplay_MouseMove(object sender, MouseEventArgs e)
        {
            mouse.X = e.X;
            mouse.Y = e.Y;
        }
        
        void mapDisplay_MouseLeave(object sender, EventArgs e)
        {
            trackMouse = false;
        }
        
        void mapDisplay_MouseEnter(object sender, EventArgs e)
        {
            trackMouse = true;
        }
        
        #endregion

        private void DrawDisplay()
        {
            if (map == null)
                return;

            if (mnuDisplayGrid.Checked)
                DrawGrid();

            if (cbShowPassability.Checked)
                DrawPassability();

            if (trackMouse)
                DrawBrush();

            if (brushMode == BrushMode.CharacterBrush && 
                rbCharacterSelect.Checked && 
                ActiveCharacter != null)
                DrawCursor(ActiveCharacter.CollisionBox.ToRectangle(), 1, 1);    
        }

        private void DrawPassability()
        {
            ActiveLayer.DrawPass(null, spriteBatch, camera, PassTexture);
        }

        private void DrawBrush()
        {
            Rectangle shadowDestination = new Rectangle(
                (int)(shadowPosition.X * Engine.TileWidth),
                (int)(shadowPosition.Y * Engine.TileHeight),
                (int)(Engine.TileWidth),
                (int)(Engine.TileHeight));

            int drawBrushWidth = 1;
            int drawBrushHeight = 1;

            switch (brushMode)
            {
                case BrushMode.TileBrush:
                    DrawTileBrush(shadowDestination);
                    drawBrushWidth = tileSelector.TileSource.Width / Engine.TileWidth;
                    drawBrushHeight = tileSelector.TileSource.Height / Engine.TileHeight;
                    break;
                case BrushMode.PassabilityBrush:
                    DrawPassabilityBrush(shadowDestination);
                    drawBrushWidth = brushWidth;
                    drawBrushHeight = brushWidth;
                    break;
                case BrushMode.CharacterBrush:
                    DrawCharacterBrush(shadowDestination);
                    drawBrushWidth = 1;
                    drawBrushHeight = 1;
                    break;
            }

            if (mnuDisplayBrushOutline.Checked)
                DrawCursor(shadowDestination, drawBrushWidth, drawBrushHeight);
        }

        private void DrawCharacterBrush(Rectangle shadowDestination)
        {
            if (rbCharacterCreate.Checked || rbCharacterClone.Checked)
            {
                if (CurrentEntityTexture != null)
                    spriteBatch.Draw(
                        CurrentEntityTexture,
                        shadowDestination,
                        new Rectangle(0, 0,
                            CurrentEntityTexture.Bounds.Width / 3,
                            CurrentEntityTexture.Bounds.Height / 4),
                        Color.White);
            }

            if (rbCharacterClone.Checked)
                DrawCursor(ActiveCharacter.CollisionBox.ToRectangle(), 1, 1);
        }

        private void DrawPassabilityBrush(Rectangle shadowDestination)
        {
            if (rbPassSimple.Checked)
            {
                DrawSimplePassBrush(shadowDestination);
            }
            else if (rbPassWalls.Checked)
            {
                DrawWallPassBrush(shadowDestination);
            }
        }

        private void DrawTileBrush(Rectangle shadowDestination)
        {
            Rectangle tilebrushDest = new Rectangle(
                shadowDestination.X, 
                shadowDestination.Y,
                tileSelector.TileSource.Width,
                tileSelector.TileSource.Height);

            spriteBatch.Draw(
                tileSelector.TilesetTexture,
                tilebrushDest,
                tileSelector.TileSource,
                Color.White);
        }

        private void DrawWallPassBrush(Rectangle shadowDestination)
        {
            Rectangle hwallDest = shadowDestination;
            Rectangle vwallDest = shadowDestination;

            if (mousePosition.Y < ((shadowDestination.Y * camera.Zoom) - camera.Position.Y) + (Engine.TileHeight * camera.Zoom) / 4)
            {
                hwallDest.Height = Engine.TileHeight / 4;
                hwallDest.Width = Engine.TileWidth * brushWidth;
                spriteBatch.Draw(
                    passTexture,
                    hwallDest,
                    Color.White);
            }
            else if (mousePosition.Y > ((shadowDestination.Y * camera.Zoom) - camera.Position.Y) + (Engine.TileHeight * camera.Zoom) * .75)
            {
                hwallDest.Y += (int)(((brushWidth - 1) * Engine.TileHeight) + (Engine.TileHeight * .75));
                hwallDest.Height = Engine.TileHeight / 4;
                hwallDest.Width = Engine.TileWidth * brushWidth;
                spriteBatch.Draw(
                    passTexture,
                    hwallDest,
                    Color.White);
            }

            if (mousePosition.X < ((shadowDestination.X * camera.Zoom) - camera.Position.X) + (Engine.TileWidth * camera.Zoom) / 4)
            {
                vwallDest.Width = Engine.TileWidth / 4;
                vwallDest.Height = Engine.TileHeight * brushWidth;
                spriteBatch.Draw(
                    passTexture,
                    vwallDest,
                    Color.White);
            }
            else if (mousePosition.X > ((shadowDestination.X * camera.Zoom) - camera.Position.X) + (Engine.TileWidth * camera.Zoom) * .75)
            {
                vwallDest.X += (int)(((brushWidth - 1) * Engine.TileWidth) + (Engine.TileWidth * .75));
                vwallDest.Width = Engine.TileWidth / 4;
                vwallDest.Height = Engine.TileHeight * brushWidth;
                spriteBatch.Draw(
                    passTexture,
                    vwallDest,
                    Color.White);
            }
        }

        private void DrawSimplePassBrush(Rectangle passDestination)
        {
            passDestination.Width = brushWidth * Engine.TileWidth;
            passDestination.Height = brushWidth * Engine.TileHeight;

            spriteBatch.Draw(
                passTexture,
                passDestination,
                Color.White);
        }

        private void DrawCursor(Rectangle destination, int hsize, int vsize)
        {
            if (hsize == 1 && vsize == 1)
                spriteBatch.Draw(
                    cursor,
                    destination,
                    Color.White);
            else
            {
                spriteBatch.Draw(
                    borderTexture,
                    new Rectangle(
                        destination.X, 
                        destination.Y, 
                        destination.Width * hsize, 
                        1),
                    Color.White);
                spriteBatch.Draw(
                    borderTexture,
                    new Rectangle(
                        destination.X, 
                        destination.Y, 
                        1, 
                        destination.Height * vsize),
                    Color.White);
                spriteBatch.Draw(
                    borderTexture,
                    new Rectangle(
                        destination.X + (destination.Width * hsize) - 1, 
                        destination.Y, 
                        1, 
                        destination.Height * vsize),
                    Color.White);
                spriteBatch.Draw(
                    borderTexture,
                    new Rectangle(
                        destination.X,
                        destination.Y + (destination.Height * vsize) - 1, 
                        destination.Width * hsize, 
                        1),
                    Color.White);

            }
        }

        private void DrawGrid()
        {
            Rectangle destination = new Rectangle(
                0,
                0,
                Engine.TileWidth,
                Engine.TileHeight);

            int maxX = (int)(mapDisplay.Width / (Engine.TileWidth * camera.Zoom)) + 1;
            int maxY = (int)(mapDisplay.Height / (Engine.TileHeight * camera.Zoom)) + 1;

            for (int y = 0; y < maxY; y++)
            {
                destination.Y = y * Engine.TileHeight;
                for (int x = 0; x < maxX; x++)
                {
                    destination.X = x * Engine.TileWidth;
                    spriteBatch.Draw(grid, destination, gridColor);
                }
            }
        }

        #region Display Rendering and Logic Region

        private void Render()
        {
            if (tileSets != null && tileSets.Count > 0)
            {
                spriteBatch.Begin(
                    SpriteSortMode.Deferred,
                    BlendState.AlphaBlend,
                    SamplerState.PointClamp,
                    null,
                    null,
                    null,
                    camera.Transformation);

                //reverse the layers because the first layer should be a the top, but if 
                //we loop through them that way it'll be drawn under the rest of the layers
                for (int i = layers.Count - 1, cnt = -1; i > cnt; i--)
                    layers[i].Draw(null, spriteBatch, camera, tileSets);

                DrawDisplay();
                spriteBatch.End();
            }
        }

        private void RenderMini()
        {
            minimapSpriteBatch.Begin(
                    SpriteSortMode.Deferred,
                    BlendState.AlphaBlend,
                    SamplerState.PointClamp,
                    null,
                    null,
                    null,
                    Matrix.Identity);

            //reverse the layers because the first layer should be a the top, but if 
            //we loop through them that way it'll be drawn under the rest of the layers
            for (int i = layers.Count - 1, cnt = -1; i > cnt; i--)
                layers[i].DrawMini(null, minimapSpriteBatch, camera, tileSets);

            minimapSpriteBatch.Draw(
                cursor,
                new Rectangle(
                    (int)(camera.Position.X / (Engine.TileWidth * camera.Zoom)),
                    (int)(camera.Position.Y / (Engine.TileHeight * camera.Zoom)),
                    (int)(camera.ViewportRectangle.Width / (Engine.TileWidth * camera.Zoom)),
                    (int)(camera.ViewportRectangle.Height / (Engine.TileHeight * camera.Zoom))),
               Color.Red);

            minimapSpriteBatch.End();
        }

        void InitializeMapScroll()
        {
            if (map != null)
            {
                mapScrollH.Maximum = Math.Max(0, map.MapWidth + 10 - (int)(camera.ViewportRectangle.Width / (Engine.TileWidth * camera.Zoom)));
                mapScrollV.Maximum = Math.Max(0, map.MapHeight + 10 - (int)(camera.ViewportRectangle.Height / (Engine.TileHeight * camera.Zoom)));

                mapScrollH.Value = (int)(camera.Position.X / (Engine.TileWidth * camera.Zoom));
                mapScrollV.Value = (int)(camera.Position.Y / (Engine.TileHeight * camera.Zoom));
            }
        }

        private void Logic()
        {
            if (layers.Count == 0)
                return;
            
            statusZoom.Text = String.Format("Zoom: {0:P}", camera.Zoom);

            if (trackMouse)
            {
                Vector2 position = GetMousePositionRelativeToCamera();
                Point tile = GetMouseHoverTile(position);

                statusMapLocation.Text = String.Format("Location: ({0},{1})", tile.X, tile.Y);

                shadowPosition = new Vector2(tile.X, tile.Y);
                mousePosition = new Vector2(mouse.X, mouse.Y);

                if (ActiveLayer != null && ActiveLayer.Visible)
                {
                    switch (brushMode)
                    {
                        case BrushMode.TileBrush:
                            TileBrushLogic(tile);
                            break;
                        case BrushMode.PassabilityBrush:
                            PassabilityBrushLogic(tile, position, camera.Zoom);
                            break;
                        case BrushMode.CharacterBrush:
                            CharacterBrushLogic(tile, position, camera.Zoom);
                            break;
                        default:
                            MessageBox.Show(string.Format("'{0}' not implemented!", brushMode));
                            break;
                    }
                }
            }
            else
            {
                statusMapLocation.Text = "Location: None";
            }
        }

        void clbLayers_ItemCheck(object sender, ItemCheckEventArgs e)
        {
            ActiveLayer.Visible = e.NewValue == CheckState.Checked;
        }

        private Vector2 GetMousePositionRelativeToCamera()
        {
            Vector2 position = camera.Position;
            position.X += mouse.X;
            position.Y += mouse.Y;

            return position;
        }

        private Point GetMouseHoverTile(Vector2 position)
        {
            Point tile = Engine.VectorToCell(position, camera.Zoom);
            tile.Y = (int)MathHelper.Clamp(tile.Y, 0, map.MapHeight - 1);
            tile.X = (int)MathHelper.Clamp(tile.X, 0, map.MapWidth - 1);
            
            return tile;
        }

        private void PassabilityBrushLogic(Point tile, Vector2 clickPosition, float zoom)
        {
            if (isLeftMouseDown)
            {
                if (rbPassSimple.Checked)
                    SetSimplePassTile(tile);
                if (rbPassWalls.Checked)
                    SetWallPassTile(tile, clickPosition, zoom);
            }
            else if (isRightMouseDown)
                DeletePassability(tile);
        }

        private void CharacterBrushLogic(Point tile, Vector2 mousePosition, float zoom)
        {
            if (isLeftMouseDown)
                SetCharacter(tile, mousePosition);
            else if (isRightMouseDown)
                ActiveLayer.RemoveEntitiesAtTile(tile);
        }

        private void TileBrushLogic(Point tile)
        {
            if (isLeftMouseDown)
            {
                if (rbTilePencil.Checked)
                    SetTiles(tile);
                else if (rbTileFill.Checked)
                    FillTiles(tile);
            }
            else if (isRightMouseDown)
            {
                if (rbTilePencil.Checked)
                    RemoveTiles(tile);
                else if (rbTileFill.Checked)
                    RemoveFillTiles(tile);
            }
        }

        private void SetCharacter(Point tile, Vector2 mousePosition)
        {
            if (lastTile != tile)
            {
                lastTile = tile;
                
                if (rbCharacterCreate.Checked)
                {
                    GenerateNewCharacter(tile);
                    rbCharacterSelect.Checked = true;
                    SetCharacterPreview();
                }
                else if (rbCharacterSelect.Checked)
                {
                    Character character = 
                        ActiveLayer.CharacterManager.GetEntityByPosition(mousePosition);

                    if (character == null)
                        ActiveCharacter = null;
                    else
                    {
                        ActiveCharacter = character;
                        MoveCharacter(tile);
                    }

                    PopulateActiveCharacterData();
                }
                else if (rbCharacterClone.Checked)
                {
                    if (ActiveCharacter == null)
                    {
                        MessageBox.Show("Please select an existing character to clone",
                            "Select a Character",
                            MessageBoxButtons.OK,
                            MessageBoxIcon.Error);
                    }
                    else
                    {
                        GenerateNewCharacter(tile);
                    }
                }
            }
        }

        private void PopulateActiveCharacterData()
        {
            lbEntityScripts.DataSource = null;

            if (ActiveCharacter != null)
            {
                tbCharacterName.Text = ActiveCharacter.Entity.EntityName;
                tbCharacterSprite.Text = ActiveCharacter.Sprite.TextureImageName;
                lbEntityScripts.DataSource = ActiveCharacter.Scripts;
                nudCharacterWidth.Value = ActiveCharacter.Sprite.Width;
                nudCharacterHeight.Value = ActiveCharacter.Sprite.Height;
                chkCharacterPassThrough.Checked = ActiveCharacter.PassThrough;
            }
            else
            {
                tbCharacterName.Text = string.Empty;
                tbCharacterSprite.Text = string.Empty;
                nudCharacterWidth.Value = RpgConstants.TileWidth;
                nudCharacterHeight.Value = RpgConstants.TileHeight;
                chkCharacterPassThrough.Checked = false;
            }
        }

        private void MoveCharacter(Point tile)
        {
            if (ActiveCharacter != null)
                ActiveCharacter.Sprite.Position = new Vector2(tile.X * Engine.TileWidth, tile.Y * Engine.TileHeight);
        }

        private void GenerateNewCharacter(Point tile)
        {
            Character character = new Character(
                CreateNewCharacterData(), 
                spriteBatch, 
                contentManager, 
                null /*ScriptContext */);
            
            character.Sprite.Position = new Vector2(tile.X * Engine.TileWidth, tile.Y * Engine.TileHeight);

            ActiveLayer.CharacterManager.Characters.Add(character);
            ActiveCharacter = character;
        }

        private CharacterData CreateNewCharacterData()
        {
            if (string.IsNullOrEmpty(tbCharacterName.Text))
            {
                tbCharacterName.Text = ActiveLayer.CharacterManager.GetUniqueEntityName();
            }
            else if (ActiveLayer.CharacterManager.HasEntity(tbCharacterName.Text))
            {
                tbCharacterName.Text = ActiveLayer.CharacterManager.GetUniqueEntityName(tbCharacterName.Text);
            }

            if (!contentManager.ContentExists(tbCharacterSprite.Text))
            {
                tbCharacterSprite.Text = InvisibleEntityTextureName;
            }

            return new CharacterData(
                GetEntityData(), 
                GetAnimatedSpriteData(), 
                GetEventScriptList(),
                chkCharacterPassThrough.Checked,
                true);
        }

        private List<EventScript> GetEventScriptList()
        {
            List<EventScript> l = new List<EventScript>();

            foreach (object item in lbEntityScripts.Items)
                l.Add((EventScript)item);

            return l;
        }

        private AnimatedSpriteData GetAnimatedSpriteData()
        {
            Dictionary<AnimationKey, AnimationData> animations = GetAnimations();

            return new AnimatedSpriteData(
                animations,
                tbCharacterSprite.Text,
                new Vector2(0, 0),
                new Vector2(0, 0),
                200f);
        }

        private Dictionary<AnimationKey, AnimationData> GetAnimations()
        {
            Dictionary<AnimationKey, AnimationData> animations = new Dictionary<AnimationKey, AnimationData>();

            int yOffset = 0;
            foreach (AnimationKey key in Enum.GetValues(typeof(AnimationKey)))
            {
                animations.Add(key, new AnimationData(
                   (int)nudCharacterFrameCount.Value,
                   (int)nudCharacterWidth.Value,
                   (int)nudCharacterHeight.Value,
                   0,
                   yOffset));

                yOffset += (int)nudCharacterHeight.Value;
            }

            return animations;
        }

        private EntityData GetEntityData()
        {
            return new EntityData(
                tbCharacterName.Text,
                10,
                10,
                10,
                10,
                10,
                10,
                "0|0|0",
                "0|0|0",
                "0|0|0");
        }

        private void SetWallPassTile(Point tile, Vector2 clickPosition, float zoom)
        {
            if (lastTile != tile)
            {
                lastTile = tile;

                float width = Engine.TileWidth * zoom;
                float height = Engine.TileHeight * zoom;

                if (tile.X * width + (width / 4) > clickPosition.X)
                {
                    //clicked on left quarter of the tile
                    SetWallPassTiles(tile, ActiveLayer, PassWall.West);
                }
                else if (tile.X * width + (width * .75) < clickPosition.X)
                {
                    //clicked on right quarter of the tile
                    SetWallPassTiles(tile, ActiveLayer, PassWall.East);
                }

                if (tile.Y * height + (height / 4) > clickPosition.Y)
                {
                    //clicked on top quarter of the tile
                    SetWallPassTiles(tile, ActiveLayer, PassWall.North);
                }
                else if (tile.Y * height + (height * .75) < clickPosition.Y)
                {
                    //clicked on bottom quarter of the tile
                    SetWallPassTiles(tile, ActiveLayer, PassWall.South);
                }
            }
        }

        private void SetWallPassTiles(Point tile, MapLayer mapLayer, PassWall wall)
        {
            switch(wall)
            {
                case PassWall.West:
                    for (int y = 0, ycnt = brushWidth; y < ycnt; y++)
                        {
                            if (!mapLayer.ClearPassWall(tile.X, tile.Y + y, (int)wall))
                                mapLayer.SetPassWall(tile.X, tile.Y + y, (int)wall);
                        }
                    break;
                case PassWall.East:
                    for (int y = 0, ycnt = brushWidth; y < ycnt; y++)
                    {
                        if (!mapLayer.ClearPassWall(tile.X + brushWidth - 1, tile.Y + y, (int)wall))
                            mapLayer.SetPassWall(tile.X + brushWidth - 1, tile.Y + y, (int)wall);
                    }
                    break;
                case PassWall.North:
                    for (int x = 0, xcnt = brushWidth; x < xcnt; x++)
                    {
                        if (!mapLayer.ClearPassWall(tile.X + x, tile.Y, (int)wall))
                            mapLayer.SetPassWall(tile.X + x, tile.Y, (int)wall);
                    }
                    break;
                case PassWall.South:
                    for (int x = 0, xcnt = brushWidth; x < xcnt; x++)
                    {
                        if (!mapLayer.ClearPassWall(tile.X + x, tile.Y + brushWidth - 1, (int)wall))
                            mapLayer.SetPassWall(tile.X + x, tile.Y + brushWidth - 1, (int)wall);
                    }
                    break;

            }
        }

        private void SetSimplePassTile(Point tile)
        {

            if (lastTile != tile)
            {
                lastTile = tile;

                for (int y = 0, ycnt = brushWidth; y < ycnt; y++)
                    for (int x = 0, xcnt = brushWidth; x < xcnt; x++)
                    {
                        if (!ActiveLayer.HasPassability(tile.X + x, tile.Y + y))
                            ActiveLayer.SetPassSimple(tile.X + x, tile.Y + y);
                    }
            }
        }

        private void DeletePassability(Point tile)
        {
            if (lastTile != tile)
            {
                lastTile = tile;

                for (int y = 0, ycnt = brushWidth; y < ycnt; y++)
                    for (int x = 0, xcnt = brushWidth; x < xcnt; x++)
                    {
                        ActiveLayer.ClearPassTile(tile.X + x, tile.Y + y);
                    }
            }
        }

        private void RemoveFillTiles(Point tile)
        {
            ActiveLayer.FloodFill(tile.X, tile.Y, new XRpgLibrary.TileEngine.Tile(-1));
        }

        private void RemoveTiles(Point tile)
        {
            int brushWidth = tileSelector.TileSource.Width / Engine.TileWidth;
            int brushHeight = tileSelector.TileSource.Height / Engine.TileHeight;

            for (
                int y = tile.Y,
                ycnt = (int)MathHelper.Clamp(tile.Y + brushWidth, 0, ActiveLayer.Height),
                brushSpanY = 0; y < ycnt; y++, brushSpanY++)
            {
                for (
                    int x = tile.X,
                    xcnt = (int)MathHelper.Clamp(tile.X + brushWidth, 0, ActiveLayer.Width),
                    brushSpanX = 0; x < xcnt; x++, brushSpanX++)
                {
                    ActiveLayer.SetTile(
                        x,
                        y,
                        -1);
                }
            }
        }

        private void FillTiles(Point startTile)
        {
            if (lastTile != startTile)
            {
                lastTile = startTile;

                ActiveLayer.FloodFill(
                    startTile.X,
                    startTile.Y,
                   tileSelector.GetSelectedMatrix(cbTileset.SelectedIndex));
                    //new XRpgLibrary.TileEngine.Tile(tileSelector.CurrentTileIndex, cbTileset.SelectedIndex));
            }
        }

        private void SetTiles(Point tile)
        {
            int brushWidth = tileSelector.TileSource.Width / Engine.TileWidth;
            int brushHeight = tileSelector.TileSource.Height / Engine.TileHeight;
            int tileIndex = tileSelector.CurrentTileIndex;
            
            for (
                int y = tile.Y, 
                ycnt = (int)MathHelper.Clamp(tile.Y + brushHeight, 0, ActiveLayer.Height),
                brushSpanY = 0; y < ycnt; y++, brushSpanY++)
            {
                for (
                    int x = tile.X,
                    xcnt = (int)MathHelper.Clamp(tile.X + brushWidth, 0, ActiveLayer.Width),
                    brushSpanX = 0; x < xcnt; x++, brushSpanX++)
                {
                    int brushTileIndex = tileIndex + brushSpanY * tileSelector.TilesWide + brushSpanX;

                    ActiveLayer.SetTile(
                        x,
                        y,
                        brushTileIndex);

                    if (cbPaintDefaultPassability.Checked)
                    {
                        ActivePassabilityLayer.SetPassTile(x, y,
                            new PassTile(
                                brushTileIndex,
                                tileSets[cbTileset.SelectedIndex].GetPassRects(brushTileIndex)));
                    }
                }
            }
            
        }
        #endregion

        private void ZoomIn_Click(object sender, EventArgs e)
        {
            camera.ZoomIn();
            InitializeMapScroll();
        }

        private void ZoomOut_Click(object sender, EventArgs e)
        {
            camera.ZoomOut();
            InitializeMapScroll();
        }

        void tabControl1_Selected(object sender, System.Windows.Forms.TabControlEventArgs e)
        {
            if (e.TabPage == tabTilesets)
            {
                brushMode = BrushMode.TileBrush;
                if (!cbKeepPassabilityVisible.Checked)
                    cbShowPassability.Checked = false;
            }
            else if (e.TabPage == tabPassability)
            {
                brushMode = BrushMode.PassabilityBrush;
                cbShowPassability.Checked = true;
            }
            else if (e.TabPage == tabCharacters)
            {
                brushMode = BrushMode.CharacterBrush;
                if (!cbKeepPassabilityVisible.Checked)
                    cbShowPassability.Checked = false;
            }
        }

        private void exitToolStripMenuItem_Click(object sender, EventArgs e)
        {
            Application.Exit();
        }

        private void saveLevelAsToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (map == null)
                return;

            FolderBrowserDialog fbDialog = new FolderBrowserDialog();
            fbDialog.Description = "Select Game Folder";
            fbDialog.SelectedPath = Application.StartupPath;
            
            if (fbDialog.ShowDialog() == DialogResult.OK)
                SaveLevel(fbDialog.SelectedPath);
        }

        private void mapScrollH_Scroll(object sender, ScrollEventArgs e)
        {
            Vector2 position = camera.Position;
            position.X = e.NewValue * Engine.TileWidth * camera.Zoom;
            camera.Position = position;
            
            mapDisplay.Invalidate();
        }

        private void mapScrollV_Scroll(object sender, ScrollEventArgs e)
        {
            Vector2 position = camera.Position;
            position.Y = e.NewValue * Engine.TileHeight * camera.Zoom;
            camera.Position = position;
            
            mapDisplay.Invalidate();
        }

        private void btnShowMiniMap_Click(object sender, EventArgs e)
        {
            minimapDisplay.Visible = !minimapDisplay.Visible;
        }

        private void lbCharacters_SelectedIndexChanged(object sender, EventArgs e)
        {
            if (ActiveCharacter == null)
                return;
            
            if (File.Exists(ActiveCharacter.Sprite.TextureImageName))
            {
                SetCharacterPreview();
            }
            else
                pbCharacterPreview.Image = null;

            PopulateActiveCharacterData();
        }

        private void SetCharacterPreview()
        {
            GDIImage sprite = ActiveCharacter.Sprite.Texture.ToImage();
            GDIImage preview = (GDIImage)new GDIBitmap(pbCharacterPreview.Width, pbCharacterPreview.Height);

            GDIRectangle dest = new GDIRectangle(0, 0, preview.Width, preview.Height);
            GDIRectangle source = new GDIRectangle(
                0,
                0,
                pbCharacterPreview.Width,
                pbCharacterPreview.Height);

            GDIGraphics g = GDIGraphics.FromImage(preview);
            g.DrawImage(sprite, dest, source, GDIGraphicsUnit.Pixel);

            pbCharacterPreview.Image = preview;

            currentEntityTexture = null;
        }

        private void statusZoom_Click(object sender, EventArgs e)
        {
            camera.ZoomTo(1f);
        }

        private void btnChooseEntitySpriteSheet_Click(object sender, EventArgs e)
        {
            OpenFileDialog ofDialog = new OpenFileDialog();

            ofDialog.Filter = "Image Files|*.BMP;*.GIF;*.JPG;*.TGA;*.PNG";
            ofDialog.CheckFileExists = true;
            ofDialog.CheckPathExists = true;
            ofDialog.Multiselect = false;
            ofDialog.InitialDirectory = Path.Combine(contentManager.RootDirectory, "PlayerSprites");

            if (ofDialog.ShowDialog() == DialogResult.OK)
                tbCharacterSprite.Text = Program.NormalizeAssetName(ofDialog.FileName);
        }

        private void tbEntitySprite_TextChanged(object sender, EventArgs e)
        {
            if (contentManager.ContentExists(tbCharacterSprite.Text))
            {
                currentEntityTexture = null;
                if (ActiveCharacter != null)
                {
                    UpdateCharacterSprite();
                }
            }
        }

        private void UpdateCharacterSprite()
        {
            ActiveLayer.CharacterManager[ActiveCharacter.Name].Sprite.Texture = CurrentEntityTexture;
            ActiveLayer.CharacterManager[ActiveCharacter.Name].Sprite.TextureImageName = tbCharacterSprite.Text;
            SetCharacterPreview();
        }

        private void UpdateCharacterDataSprite()
        {
            if (contentManager.ContentExists(tbCharacterSprite.Text))
            {
                ActiveCharacter.Sprite.TextureImageName = tbCharacterSprite.Text;
            }
        }

        private void UpdateCharacterDataName()
        {
            ActiveLayer.CharacterManager[ActiveCharacter.Name].Entity.EntityName = tbCharacterName.Text;
        }


        void chkCharacterPassThrough_LostFocus(object sender, System.EventArgs e)
        {
            if (ActiveCharacter != null)
            {
                ActiveCharacter.PassThrough = chkCharacterPassThrough.Checked;
            }
        }

        void tbCharacterName_LostFocus(object sender, System.EventArgs e)
        {
            if (ActiveCharacter != null && ActiveCharacter.Name != tbCharacterName.Text)
            {
                if (ActiveLayer.CharacterManager.HasEntity(tbCharacterName.Text))
                {
                    MessageBox.Show(
                        String.Format("There is already a character by the name '{0}'. Please choose a unique name.", tbCharacterName.Text),
                        "Character Name in use",
                        MessageBoxButtons.OK,
                        MessageBoxIcon.Error);
                    
                    tbCharacterName.Text = ActiveCharacter.Name;
                }

                if (ActiveCharacter != null)
                {
                    UpdateCharacterDataName();
                    BindCharacterList();
                    SetCharacterPreview();
                }
            }
        }

        void nudCharacterSize_LostFocus(object sender, System.EventArgs e)
        {
            if (ActiveCharacter != null)
            {
                AnimatedSprite newSprite = new AnimatedSprite(GetAnimatedSpriteData(), contentManager);
                newSprite.SpriteBatch = spriteBatch;
                newSprite.Position = ActiveLayer.CharacterManager[ActiveCharacter.Name].Sprite.Position;
                
                ActiveLayer.CharacterManager[ActiveCharacter.Name].Sprite = newSprite;
            }
        }

        private void BindCharacterList()
        {
            lbCharacters.DataSource = null;
            lbCharacters.DataSource = ActiveLayer.CharacterManager.Characters;
            lbCharacters.DisplayMember = "Name";
        }

        void FormMain_KeyUp(object sender, System.Windows.Forms.KeyEventArgs e)
        {
            keyControlPressed = e.Control;
            keyAltPressed = e.Alt;
        }

        void FormMain_KeyDown(object sender, System.Windows.Forms.KeyEventArgs e)
        {
            keyControlPressed = e.Control;
            keyAltPressed = e.Alt;
        }

        private void btnMoveMapLayerUp_Click(object sender, EventArgs e)
        {
            if (mapLayerView.SelectedRows.Count < 1 || mapLayerView.SelectedRows[0].Index < 1)
                return;

            int oldIndex = mapLayerView.SelectedRows[0].Index;
            int newIndex = oldIndex - 1;

            ReorderLayer(oldIndex, newIndex);       
        }

        private void btnMapLayerDown_Click(object sender, EventArgs e)
        {
            if (mapLayerView.SelectedRows.Count < 0 
                || mapLayerView.SelectedRows[0].Index > mapLayerView.Rows.Count - 2)
                return;

            int oldIndex = mapLayerView.SelectedRows[0].Index;
            int newIndex = oldIndex + 1;

            ReorderLayer(oldIndex, newIndex);     
        }

        private void ReorderLayer(int oldIndex, int newIndex)
        {
            MapLayer movingLayer = layers[oldIndex];

            layers.RemoveAt(oldIndex);
            layers.Insert(newIndex, movingLayer);

            mapLayerView.SelectRow(newIndex);
        }

        private void btnAddEntityScript_Click(object sender, EventArgs e)
        {
            FormWireScript frmWireScript = new FormWireScript();
            frmWireScript.ShowDialog();

            if (frmWireScript.DialogResult == DialogResult.OK)
            {
                ActiveCharacter.Scripts.Add(new EventScript(frmWireScript.Event, frmWireScript.ScriptName, null));
                PopulateActiveCharacterData();
            }
        }

        private void btnDeleteEntityScript_Click(object sender, EventArgs e)
        {
            if (lbEntityScripts.SelectedIndex > -1)
            {
                ActiveCharacter.Scripts.RemoveAt(lbEntityScripts.SelectedIndex);
                PopulateActiveCharacterData();

                if (lbEntityScripts.Items.Count > 0)
                    lbEntityScripts.SelectedIndex = 0;
            }
        }

        private void rbCharacterCreate_CheckedChanged(object sender, EventArgs e)
        {
            if (rbCharacterCreate.Checked)
            {
                ActiveCharacter = null;
                currentEntityTexture = null;
                PopulateActiveCharacterData();
                tbCharacterName.Focus();
            }
        }

        private void exportImageToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (layers.Count > 0)
            {
                FormExport export = new FormExport(layers.ToList<MapLayer>(), tileSets, GraphicsDevice);
                export.ShowDialog();
            }
            else
            {
                MessageBox.Show("Please add at least one layer to export");
            }
        }

        private void lbEntityScripts_SelectedIndexChanged(object sender, EventArgs e)
        {
            scriptParameterInput.Script = string.Empty;

            if (lbEntityScripts.SelectedIndex > -1)
            {
                scriptParameterInput.Values = ActiveCharacter.Scripts[lbEntityScripts.SelectedIndex].ScriptArguments;
                scriptParameterInput.Script = ActiveCharacter.Scripts[lbEntityScripts.SelectedIndex].ScriptName;
            }
        }

        void scriptParameterInput_Leave(object sender, System.EventArgs e)
        {
            EventScript eventScript = ((EventScript)lbEntityScripts.SelectedItem);
            eventScript.ScriptArguments = scriptParameterInput.GetValuesArray();

            ActiveCharacter.Scripts[lbEntityScripts.SelectedIndex] = eventScript;
        }

        private void restoreDefaultPassabilityForAllTiles_Click(object sender, EventArgs e)
        {
            RefreshTilePassability();
            ActiveLayer = ActivePassabilityLayer;
            tabControl1.SelectedTab = tabPassability;
        }

        private void restoreDefaultPassabilityForSelectedTiles_Click(object sender, EventArgs e)
        {
            RefreshTilePassability(
                tileSelector.GetSelectedMatrix(cbTileset.SelectedIndex));
            ActiveLayer = ActivePassabilityLayer;
            tabControl1.SelectedTab = tabPassability;
        }

        private void RefreshTilePassability()
        {
            RefreshTilePassability(null);
        }

        /// <summary>
        /// refactor me into the MapLayer class
        /// </summary>
        /// <param name="selectedTiles"></param>
        private void RefreshTilePassability(XRpgLibrary.TileEngine.Tile[,] selectedTiles)
        {
            for (int y = 0, ycnt = ActiveLayer.Height; y < ycnt; y++)
                for (int x = 0, xcnt = ActiveLayer.Width; x < xcnt; x++)
                {
                    XRpgLibrary.TileEngine.Tile tile = ActiveLayer.GetTile(x, y);

                    if (selectedTiles == null || selectedTiles.Contains(tile))
                    {
                        List<Rectangle> passRects = null;

                        if (ActiveLayer.TilesetIndex > -1)
                            passRects = tileSets[ActiveLayer.TilesetIndex].GetPassRects(tile.TileIndex);

                        ActivePassabilityLayer.SetPassTile(
                            x, y,
                            new PassTile(
                                tile.TileIndex,
                                /*tile.Tileset,*/
                                passRects)
                                );
                    }
                }
        }

        private void duplicateActiveLayerToolStripMenuItem_Click(object sender, EventArgs e)
        {
            int newIndex = mapLayerView.ActiveLayerIndex;
            MapLayer newLayer = ActiveLayer.Clone(null, spriteBatch, contentManager);
            layers.Insert(newIndex, newLayer);
            mapLayerView.SelectRow(newIndex);
        }

        private void tbTilesetAnimationScript_TextChanged(object sender, System.EventArgs e)
        {
            tileSetData[cbTileset.SelectedIndex].AnimationScript = tbTilesetAnimationScript.Text
                .Replace("\r\n", "\n")
                .Replace("\n\n","\n");
        }
    }
}